type SingleColorRGB = {R: number, G: number, B: number};
type SingleColorHSB = {H: number, S: number, B: number};

/**
 * 向往阳光的颜色管理器引擎
 */
class ColorManager {
    /**
     * @return {number}
     */
    static RGBToRealHEX = (r:number, g:number, b:number) => {
        return ColorManager.colorHEXToRealHEX(ColorManager.RGBToHEX(r, g, b));
    };

    /**
     * RGB转十六进制的工具，以#开头形式返回
     * @param r 红
     * @param g 绿
     * @param b 蓝
     * @constructor
     * @date 2019-02-16
     */
    static RGBToHEX = (r:number, g:number, b:number):string => {
        r = Math.round(r); g = Math.round(g); b = Math.round(b);
        let cRange = function (v:number) {
            if (v < 0) v = 0;
            if (v > 255) v = 255;
            return v;
        };

        let toHex = function (v:any) {
            let rr = v.toString(16);
            while (rr.length < 2) {
                rr = "0".concat(rr);
            }
            return rr;
        };
        r = cRange(r);
        g = cRange(g);
        b = cRange(b);
        return "#".concat(toHex(r), toHex(g), toHex(b));
    };

    /**
     * 颜色格式的十六进制转为真实的十六进制
     * @param stringHEX
     */
    static colorHEXToRealHEX = (stringHEX:String):number => {
        let newHEXString = "0x" + stringHEX.replace("#", "");
        return Number(newHEXString);
    };

    /**
     * 十六进制转RGB的转换工具
     * @param hex
     * @returns {number[]}
     * @constructor
     * @date 2019-02-16
     */
    static HEXToRGB = (hex:string):SingleColorRGB => {
        let rString = 'FF', gString = 'FF', bString = 'FF';
        if (hex.length === 7) {
            rString = hex.substr(1, 2);
            gString = hex.substr(3, 2);
            bString = hex.substr(5, 2);
        } else {
            let rStr = hex.substr(1, 1);
            let gStr = hex.substr(2, 1);
            let bStr = hex.substr(3, 1);
            rString = rStr + rStr;
            gString = gStr + gStr;
            bString = bStr + bStr;
        }
        let R = parseInt(rString, 16);
        let G = parseInt(gString, 16);
        let B = parseInt(bString, 16);

        return {R, G, B};
    };

    /**
     * 由HSB转RGB
     * @param hsb_h
     * @param hsb_s
     * @param hsb_b
     * @constructor
     */
    static HSBToRGB = (hsb_h:number, hsb_s:number, hsb_b:number):SingleColorRGB => {
        let rgb_r:number = 0, rgb_g:number = 0, rgb_b:number = 0;
        hsb_h = hsb_h % 360;
        if (hsb_s > 100) hsb_s = 100;
        if (hsb_b > 100) hsb_b = 100;
        if (hsb_s < 0) hsb_s = 0;
        if (hsb_b < 0) hsb_b = 0;
        let h = Math.floor(hsb_h / 60);
        let maximum_RGB = hsb_b / 100 * 255;
        let minimum_RGB = (1 - hsb_s / 100) * maximum_RGB;
        let restValue = (hsb_h - h * 60) / 60;
        let middle_RGB = 0;
        if (h % 2 === 0) {
            middle_RGB = minimum_RGB + (maximum_RGB - minimum_RGB) * restValue;
        } else
            middle_RGB = maximum_RGB - (maximum_RGB - minimum_RGB) * restValue;
        switch (h) {
            case 0:
                rgb_r = maximum_RGB;
                rgb_g = middle_RGB;
                rgb_b = minimum_RGB;
                break;
            case 1:
                rgb_r = middle_RGB;
                rgb_g = maximum_RGB;
                rgb_b = minimum_RGB;
                break;
            case 2:
                rgb_r = minimum_RGB;
                rgb_g = maximum_RGB;
                rgb_b = middle_RGB;
                break;
            case 3:
                rgb_r = minimum_RGB;
                rgb_g = middle_RGB;
                rgb_b = maximum_RGB;
                break;
            case 4:
                rgb_r = middle_RGB;
                rgb_g = minimum_RGB;
                rgb_b = maximum_RGB;
                break;
            case 5:
                rgb_r = maximum_RGB;
                rgb_g = minimum_RGB;
                rgb_b = middle_RGB;
                break;
        }
        return { R:Math.round(rgb_r), G: Math.round(rgb_g), B: Math.round(rgb_b)};
    };

    //RGB转HSB
    static RGBToHSB = (r:number, g:number, b:number) => {
        let cRange = function (v:number) {
            if (v < 0) v = 0;
            if (v > 255) v = 255;
            return v;
        };

        r = cRange(r);
        g = cRange(g);
        b = cRange(b);

        if (r <= 0 && g <= 0 && b <= 0) {
            return {H: 0, S: 0, B: 0};
        }

        if (r >= 255 && g >= 255 && b >= 255) {
            return {H: 0, S: 0, B: 100};
        }

        let HValue = 0, SValue = 0, BValue = 0;
        let rNormal = r / 255, gNormal = g / 255, bNormal = b / 255; //RGB标准化之后得到的值，取值范围在0-1之间
        let minNormal = Math.min(rNormal, gNormal, bNormal); //标准化RGB中的最小值
        let maxNormal = Math.max(rNormal, gNormal, bNormal); //标准化RGB中的最大值


        let rS = rNormal - minNormal, gS = gNormal - minNormal, bS = bNormal - minNormal; //标准化的RGB值处理之后，rS、gS、bS中的最小值为0

        let maxS = Math.max(rS, gS, bS);

        let rSN = rS / maxS, gSN = gS / maxS, bSN = bS / maxS; //rSN、gSN、bSN中的最大值为1，最小值为0

        if (minNormal === 0) SValue = 100;
        else SValue = (1 - minNormal / maxNormal) * 100;
        //最小值为r，即HValue取值120-240
        if (rS === 0) {
            if (gSN === 1) HValue = 120 + 60 * bSN;
            if (bSN === 1) HValue = 180 + 60 * (1 - gSN);
        }

        //最小值为g，即HValue取值240-360
        if (gS === 0) {
            if (bSN === 1) HValue = 240 + 60 * rSN;
            if (rSN === 1) HValue = 300 + 60 * (1 - bSN);
        }

        //最小值为b，即HValue取值0-120
        if (bS === 0) {
            if (rSN === 1) HValue = 60 * gSN;
            if (gSN === 1) HValue = 60 + 60 * (1 - rSN);
        }

        BValue = maxNormal * 100; //bright

        return {H: Math.round(HValue), S: Math.round(SValue), B: Math.round(BValue)};
    };

    static HSBToHEX = (h:number, s:number, b:number) => {
        let rgb = ColorManager.HSBToRGB(h, s, b);
        let hex = ColorManager.RGBToHEX(rgb.R, rgb.G, rgb.B);
        return hex;
    };

    static HSBToHEXNumber = (h:number, s:number, b:number) => {
        let rgb = ColorManager.HSBToRGB(h, s, b);
        let hex = ColorManager.RGBToHEX(rgb.R, rgb.G, rgb.B);
        return ColorManager.colorHEXToRealHEX(hex);
    };

    static getRGBGradientColor = (colorBoard:Array<SingleColorRGB>, ratio:number):SingleColorRGB => {
        let segment = colorBoard.length - 1;
        let volume = ratio * segment;
        let startColor:SingleColorRGB = {R: 0, G: 0, B: 0},
            endColor:SingleColorRGB = {R: 0, G: 0, B: 0};
        let selectedColor;
        let singleRatio = 0;
        for (let cb = 0; cb < colorBoard.length; cb++) {
            if (!colorBoard[cb] || !colorBoard[cb + 1]) continue;
            if (cb < volume && volume <= cb + 1) {
                startColor = colorBoard[cb];
                endColor = colorBoard[cb + 1];
                singleRatio = volume - cb;
                break;
            }
        }

        if (ratio <= 0) return colorBoard[0];
        if (ratio >= 1) return colorBoard[colorBoard.length - 1];

        selectedColor = {
            R: Math.round(startColor["R"] + singleRatio * (endColor["R"] - startColor["R"])),
            G: Math.round(startColor["G"] + singleRatio * (endColor["G"] - startColor["G"])),
            B: Math.round(startColor["B"] + singleRatio * (endColor["B"] - startColor["B"]))
        };

        return selectedColor;
    };

    /**
     * 由色相、饱和度和亮度而产生的线性过度颜色
     * @param colorBoard
     * @param quantity
     * @returns {Array}
     * @date 2019-03-11
     */
    static getHSBColorArray = (colorBoard:Array<SingleColorHSB>, quantity:number) => {
        let returnValue = [];
        let singleRange = Math.round(quantity / (colorBoard.length - 1));

        if (singleRange <= 0)
            singleRange = 1;


        for (let b = 0; b < colorBoard.length; b++) {
            let firstColorBoard = colorBoard[b], secondColorBoard = colorBoard[b+1];
            if (firstColorBoard !== undefined && secondColorBoard !== undefined) {
                for (let sr = 0; sr < singleRange; sr++) {
                    let hsb_h = firstColorBoard["H"] + sr / singleRange * (secondColorBoard["H"] - firstColorBoard["H"]);
                    let hsb_s = firstColorBoard["S"] + sr / singleRange * (secondColorBoard["S"] - firstColorBoard["S"]);
                    let hsb_b = firstColorBoard["B"] + sr / singleRange * (secondColorBoard["B"] - firstColorBoard["B"]);
                    let rgbValue = ColorManager.HSBToRGB(hsb_h, hsb_s, hsb_b);

                    if (returnValue.length < quantity)
                        returnValue.push(rgbValue);
                }
            }
        }
        return returnValue;
    };
}

export {ColorManager, SingleColorHSB, SingleColorRGB};